programming4us
           
 
 
Programming

Microsoft ASP.NET 3.5 : Writing HTTP Handlers (part 4) - Serving Images More Effectively

- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019
5/17/2012 3:54:45 PM

Serving Images More Effectively

Any page we get from the Web today is topped with so many images and is so well conceived and designed that often the overall page looks more like a magazine advertisement than an HTML page. Looking at the current pages displayed by portals, it’s rather hard to imagine there ever was a time—and it was only seven or eight years ago—when one could create a Web site by using only a text editor and some assistance from a friend who had a bit of familiarity with Adobe PhotoShop.

In spite of the wide use of images on the Web, there is just one way in which a Web page can reference an image—by using the HTML <img> tag. By design, this tag points to a URL. As a result, to be displayable within a Web page, an image must be identifiable through a URL and its bits should be contained in the output stream returned by the Web server for that URL.

In many cases, the URL points to a static resource such as a GIF or JPEG file. In this case, the Web server takes the request upon itself and serves it without invoking external components. However, the fact that many <img> tags on the Web are bound to a static file does not mean there’s no other way to include images in Web pages.

Where else can you turn to get images aside from picking them up from the server file system? For example, you can load images from a database or you can generate or modify them on the fly just before serving the bits to the browser.

Loading Images from Databases

The use of a database as the storage medium for images is controversial. Some people have good reasons to push it as a solution; others tell you bluntly they would never do it and that you shouldn’t either. Some people can tell you wonderful stories of how storing images in a properly equipped database was the best experience of their professional life. With no fear that facts could perhaps prove them wrong, other people will confess that they would never use a database again for such a task.

The facts say that all database management systems (DBMS) of a certain reputation and volume have supported binary large objects (BLOB) for quite some time. Sure, a BLOB field doesn’t necessarily contain an image—it can contain a multimedia file or a long text file—but overall there must be a good reason for having this BLOB support in SQL Server, Oracle, and similar popular DBMS systems!

To read an image from a BLOB field with ADO.NET, you execute a SELECT statement on the column and use the ExecuteScalar method to catch the result and save it in an array of bytes. Next, you send this array down to the client through a binary write to the response stream. Let’s write an HTTP handler to serve a database-stored image:

public class DbImageHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext ctx)
    {
        // Ensure the URL contains an ID argument that is a number
        int id = -1;
        bool result = Int32.TryParse(ctx.Request.QueryString["id"], out id);
        if (!result)
           ctx.Response.End();

        string connString = "...";
        string cmdText = "SELECT photo FROM employees WHERE employeeid=@id";

        // Get an array of bytes from the BLOB field
        byte[] img = null;
        SqlConnection conn = new SqlConnection(connString);
        using (conn)
        {
            SqlCommand cmd = new SqlCommand(cmdText, conn);
            cmd.Parameters.AddWithValue("@id", id);
            conn.Open();
            img = (byte[])cmd.ExecuteScalar();
            conn.Close();
        }
        // Prepare the response for the browser
        if (img != null)
        {
            ctx.Response.ContentType = "image/jpeg";
            ctx.Response.BinaryWrite(img);
        }
    }

    public bool IsReusable
    {
        get { return true; }
    }
}


					  

There are quite a few assumptions made in this code. First, we assume that the field named photo contains image bits and that the format of the image is JPEG. Second, we assume that images are to be retrieved from a fixed table of a given database through a predefined connection string. Finally, we’re assuming that the URL to invoke this handler includes a query string parameter named id.

Notice the attempt to convert the value of the id query parameter to an integer before proceeding. This simple check significantly reduces the surface attack for malicious users by verifying that what is going to be used as a numeric ID is really a numeric ID. Especially when you’re inoculating user input into SQL query commands, filtering out extra characters and wrong data types is a fundamental measure for preventing attacks.

The BinaryWrite method of the HttpResponse object writes an array of bytes to the output stream.

Warning

If the database you’re using is Northwind (as in the preceding example), an extra step might be required to ensure that the images are correctly managed. For some reason, the SQL Server version of the Northwind database stores the images in the photo column of the Employees table as OLE objects. This is probably because of the conversion that occurred when the database was upgraded from the Microsoft Access version. As a matter fact, the array of bytes you receive contains a 78-byte prefix that has nothing to do with the image. Those bytes are just the header created when the image was added as an OLE object to the first version of Access. Although the preceding code works like a champ with regular BLOB fields, it must undergo the following modification to work with the photo field of the Northwind.Employees database:

Response.OutputStream.Write(img, 78, img.Length);

Instead of using the BinaryWrite call, which doesn’t let you specify the starting position, use the code shown here.


A sample page to test BLOB field access is shown in Figure 6. The page lets users select an employee ID and post back. When the page renders, the ID is used to complete the URL for the ASP.NET Image control.

string url = String.Format("dbimage.axd?id={0}",
                           DropDownList1.SelectedValue);
Image1.ImageUrl = url;

Figure 6. Downloading images stored within the BLOB field of a database.

An HTTP handler must be registered in the web.config file and bound to a public endpoint. In this case, the endpoint is dbimage.axd and the script to enter in the configuration file is shown next:

<httpHandlers>
    <add verb="*" path="dbimage.axd"
         type="Core35.Components.DbImageHandler,Core35Lib"/>
</httpHandlers>

Note

The preceding handler clearly has a weak point: it hard-codes a SQL command and the related connection string. This means that you might need a different handler for each different command or database to access. A more realistic handler would probably use an external and configurable database-specific provider. Such a provider can be as simple as a class that implements an agreed interface. At a minimum, the interface will supply a method to retrieve and return an array of bytes. Alternatively, if you want to keep the ADO.NET code in the handler itself, the interface will just supply members that specify the command text and connection string. The handler will figure out its default provider from a given entry in the web.config file.


Serving Dynamically Generated Images

Isn’t it true that an image is worth thousands of words? Many financial Web sites offer charts and, more often than not, these charts are dynamically generated on the server. Next, they are served to the browser as a stream of bytes and travel over the classic response output stream. But can you create and manipulate server-side images? For these tasks, Web applications normally rely on ad hoc libraries or the graphic engine of other applications (for example, Microsoft Office applications).

ASP.NET applications are different and, to some extent, luckier. ASP.NET applications, in fact, can rely on a powerful and integrated graphic engine capable of providing an object model for image generation. This server-side system is GDI+, and contrary to what some people might have you believe, GDI+ is fair game for generating images on the fly for ASP.NET applications.

As its name suggests, GDI+ is the successor of GDI, the Graphics Device Interface included with versions of the Windows operating system that shipped before Windows XP. The .NET Framework encapsulates the key GDI+ functionalities in a handful of managed classes and makes those functions available to Web, Windows Forms, and Web service applications.

Most of the GDI+ services belong to the following categories: 2D vector graphics and imaging. 2D vector graphics involve drawing simple figures such as lines, curves, and polygons. Under the umbrella of imaging are functions to display, manipulate, save, and convert bitmap and vector images. Finally, a third category of functions can be identified—typography, which includes the display of text in a variety of fonts, sizes, and styles. Having the goal of creating images dynamically, we are most interested in drawing figures and text and in saving the work as JPEGs or GIFs.

In ASP.NET, writing images to disk might require some security adjustments. Normally, the ASP.NET runtime runs under the aegis of the NETWORK SERVICE user account. In the case of anonymous access with impersonation disabled—which are the default settings in ASP.NET—the worker process lends its own identity and security token to the thread that executes the user request of creating the file. With regard to the default scenario, an access denied exception might be thrown if NETWORK SERVICE lacks writing permissions on virtual directories—a pretty common situation.

ASP.NET and GDI+ provide an interesting alternative to writing files on disk without changing security settings: in-memory generation of images. In other words, the dynamically generated image is saved directly to the output stream in the needed image format or in a memory stream.

Writing Copyright Notes on Images

GDI+ supports quite a few image formats, including JPEG, GIF, BMP, and PNG. The whole collection of image formats is in the ImageFormat structure from the System.Drawing namespace. You can save a memory-resident Bitmap object to any of the supported formats by using one of the overloads of the Save method:

Bitmap bmp = new Bitmap(file);
...
bmp.Save(outputStream, ImageFormat.Gif);

When you attempt to save an image to a stream or disk file, the system attempts to locate an encoder for the requested format. The encoder is a GDI+ module that converts from the native format to the specified format. Note that the encoder is a piece of unmanaged code that lives in the underlying Win32 platform. For each save format, the Save method looks up the right encoder and proceeds.

The next example wraps up all the points we touched on. This example shows how to load an existing image, add some copyright notes, and serve the modified version to the user. In doing so, we’ll load an image into a Bitmap object, obtain a Graphics for that bitmap, and use graphics primitives to write. When finished, we’ll save the result to the page’s output stream and indicate a particular MIME type.

The sample page that triggers the example is easily created, as shown in the following listing:

<html>
<body>
    <img id="picture" src="dynimage.axd?url=images/pic1.jpg" />
</body>
</html>

The page contains no ASP.NET code and displays an image through a static HTML <img> tag. The source of the image, though, is an HTTP handler that loads the image passed through the query string, and then manipulates and displays it. Here’s the source code for the ProcessRequest method of the HTTP handler:

public void ProcessRequest (HttpContext context)
{
    object o = context.Request["url"];
    if (o == null)
    {
        context.Response.Write("No image found.");
        context.Response.End();
        return;
    }

    string file = context.Server.MapPath((string)o);
    string msg = ConfigurationManager.AppSettings["CopyrightNote"];
    if (File.Exists(file))
    {
        Bitmap bmp = AddCopyright(file, msg);
        context.Response.ContentType = "image/jpeg";
        bmp.Save(context.Response.OutputStream, ImageFormat.Jpeg);
        bmp.Dispose();
    }
    else
    {
        context.Response.Write("No image found.");
        context.Response.End();
    }
}


					  

Note that the server-side page performs two different tasks indeed. First, it writes copyright text on the image canvas; next, it converts whatever the original format was to JPEG:

Bitmap AddCopyright(string file, string msg)
{
    // Load the file and create the graphics
    Bitmap bmp = new Bitmap(file);
    Graphics g = Graphics.FromImage(bmp);

    // Define text alignment
    StringFormat strFmt = new StringFormat();
    strFmt.Alignment = StringAlignment.Center;

    // Create brushes for the bottom writing
    // (green text on black background)
    SolidBrush btmForeColor = new SolidBrush(Color.PaleGreen);
    SolidBrush btmBackColor = new SolidBrush(Color.Black);

    // To calculate writing coordinates, obtain the size of the
    // text given the font typeface and size
    Font btmFont = new Font("Verdana", 7);
    SizeF textSize = new SizeF();
    textSize = g.MeasureString(msg, btmFont);

    // Calculate the output rectangle and fill
    float x = ((float) bmp.Width-textSize.Width-3);
    float y = ((float) bmp.Height-textSize.Height-3);
    float w = ((float) x + textSize.Width);
    float h = ((float) y + textSize.Height);
    RectangleF textArea = new RectangleF(x, y, w, h);
    g.FillRectangle(btmBackColor, textArea);

    // Draw the text and free resources
    g.DrawString(msg, btmFont, btmForeColor, textArea);
    btmForeColor.Dispose();
    btmBackColor.Dispose();
    btmFont.Dispose();
    g.Dispose();

    return bmp;
}


					  

Figure 7 shows the results.

Figure 7. A server-resident image has been modified before being displayed.

Note that the additional text is part of the image the user downloads on her client browser. If the user saves the picture by using the Save Picture As menu from the browser, the text (in this case, the copyright note) is saved along with the image.

Note

What if the user requests the JPG file directly from the address bar? And what if the image is linked by another Web site or referenced in a blog post? In these cases, the original image is served without any further modification. Why is it so? As mentioned, for performance reasons IIS serves static files, such as JPG images, directly without involving any external module, including the ASP.NET runtime. The HTTP handler that does the trick of adding a copyright note is therefore blissfully ignored when the request is made via the address bar or a hyperlink. What can you do about it?

In IIS 6.0, you must register the JPG extension as an ASP.NET extension for a particular application using the IIS Manager as shown in Figure 18-4. In this case, each request for JPG resources is forwarded to your application and resolved through the HTTP handler.

In IIS 7.0, things are even simpler for developers. All that you have to do is add the following lines to the application’s web.config file:

<system.webServer>
    <handlers>
        <add verb="*"
            path="*.jpg"
            type="Core35.Components.DynImageHandler,Core35Lib" />
    </handlers>
</system.webServer>

The system.webServer section is a direct child of the root configuration node.

Other -----------------
- Microsoft ASP.NET 3.5 : HTTP Handlers and Modules - Quick Overview of the IIS Extensibility API
- Programming WCF Services : Queued Services - The HTTP Bridge
- Microsoft ASP.NET 4 : Ajax - Extender Controls (part 2) - A Modal Pop-up Dialog-Style Component
- Microsoft ASP.NET 4 : Ajax - Extender Controls (part 1) - The AutoComplete Extender
- Mobile Handheld Devices : DATA SYNCHRONIZATION
- Mobile Handheld Devices : MEMORY, STORAGE AND BATTERIES
- LINQ to Objects : How to Return Elements When the Result Is a Sequence (Select Many)
- LINQ to Objects : How to Change the Return Type (Select Projection)
- A Technical Overview of the Mobile Web : OTHER MOBILE TECHNOLOGIES
- A Technical Overview of the Mobile Web : THE MOBILE NETWORK
- Programming WCF Services : The Response Service (part 4) - Transactions
- Programming WCF Services : The Response Service (part 3) - Queued Service-Side Programming & Response Service-Side Programming
- Programming WCF Services : The Response Service (part 2) - Client-Side Programming
- Programming WCF Services : The Response Service (part 1) - Designing a Response Service Contract
- Programming WCF Services : Queued Versus Connected Calls - Requiring Queuing
- Programming WCF Services : Queued Services - Playback Failures
- DotNetNuke Skinning : Package and Deploy
- Unit Testing in Visual Studio 2010 (part 2) - Running a battery of tests
- Unit Testing in Visual Studio 2010 (part 1) - Creating unit tests
- Microsoft ASP.NET 3.5 : AJAX-Enabled Web Services - Remote Calls via Page Methods
 
 
 
Top 10
 
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
programming4us programming4us